<?php

/******************************************************************

	Name: 数据库操作类 ( 基于 PDO )
	Author: 徐立 ( Eks Tsui )
	Email: ekstsui@gmail.com
	QQ: 1181601

/******************************************************************/

/* 示例 :

$db = new db ( 'mysql:host=127.0.0.1;port=3306;', 'root', 'password', 'database_name', true, 'utf8' );

*/

if ( !defined ( 'IN_SYSTEM' ) )
{
	exit ( 'Access Denied' );
}

class db
{
	var $id;
	var $dsn;
	var $user;
	var $password;
	var $database;
	var $charSet;
	var $ignoreError;
	var $attributes;

	var $conn; // 数据库连接
	var $queryCount; // 查询次数
	var $affectedRows; // 影响行数 ( 每次 Query 后改变 )
	var $debug; // 调试模式
	var $debugLineSplit = '<br />';
	var $charSplit = '`';
	var $inTransaction = false;
	var $readOnly = false; // 是否只读

	var $startTime;
	var $endTime;
	var $statSql;
	var $statSqlLimit = 10; // 历史 Query 条数

	var $traceEnabled = false;
	var $tracer = null;

	var $dbConfigs = array (); // 预备数据库配置
	var $dbIdx = 0;
	var $dbIdxArray = array ();
	var $multiFlag = false;

	var $writeOperations = array ( 'ALTER ', 'CREATE ', 'DROP ', 'DELETE ', 'INSERT ', 'REPLACE ', 'TRUNCATE ', 'UPDATE ' ); // 定义写操作

	// 初始化数据库配置
	function db ( $dsn, $user = '', $password = '', $database = null, $autoCommit = false, $charSet = null, $persistent = false, $ignoreError = false, $timeout = 10 )
	{
		if ( is_array ( $dsn ) )
		{
			$this->db_multi ( $dsn );
		}
		else
		{
			$this->dsn = $dsn;
			$this->user = $user;
			$this->password = $password;
			$this->database = $database;
			$this->charSet = $charSet;
			$this->ignoreError = $ignoreError;

			$this->id = md5 ( $this->dsn . $this->user . $this->database );

			$this->attributes = array (
				PDO::ATTR_AUTOCOMMIT => $autoCommit,
				PDO::ATTR_PERSISTENT => $persistent,
				PDO::ATTR_TIMEOUT => $timeout,
				);

			$this->conn = null;
			$this->queryCount = 0;
			$this->startTime = '';
			$this->statSql = array();
			$this->endTime = '';
		}
	}

	// 初始化随极数据库配置
	function db_multi ( $dbConfigs = null )
	{
		$this->multiFlag = true;

		if ( is_array ( $dbConfigs ) )
		{
			$this->dbConfigs = $dbConfigs;
			$this->dbIdxArray = array_keys ( $this->dbConfigs );
			shuffle ( $this->dbIdxArray );
		}

		$dbIdxSession = $_COOKIE['db_idx_session'];
		if ( is_numeric ( $dbIdxSession ) )
		{
			$this->dbIdx = intval ( $dbIdxSession );
		}
		else
		{
			$this->dbIdx = array_shift ( $this->dbIdxArray );
		}

		$dbConfig = $this->dbConfigs[$this->dbIdx];
		$this->db ( $dbConfig['dsn'], $dbConfig['user'], $dbConfig['password'], $dbConfig['database'], $dbConfig['auto_commit'], $dbConfig['charset'], $dbConfig['persistent'] );
	}

	// 连接数据库
	function connect ()
	{
		$conn = &$this->conn;
		$dsn = $this->dsn;

		if ( !$conn )
		{
			if ( $this->traceEnabled )
			{
				if ( !$this->tracer )
				{
					include_once ( './class/class_tracer.php' );
					$this->tracer = new tracer ( $this->dsn . $this->database );
				}
			}
			try
			{
				$conn = new PDO ( $dsn, $this->user, $this->password, $this->attributes );
				if ( $conn && $this->multiFlag ) setcookie ( 'db_idx_session', $this->dbIdx );
			}
			catch ( PDOException $e )
			{
				if ( count ( $this->dbIdxArray ) > 0 && $this->multiFlag )
				{
					setcookie ( 'db_idx_session', null );
					$_COOKIE['db_idx_session'] = null;

					$this->db_multi ();
					$this->connect ();
				}
				elseif ( !$this->ignoreError )
				{
					echo ( "<ERROR><div style='padding:20px;'>服务器忙，请稍后访问！</div>" );
					throw ( $e );
				}
			}

			$this->useDatabase ( $this->database, $conn );
			if ( $this->charSet && $conn ) $conn->query ( "set names '{$this->charSet}';" );
			$this->begin ();
		}
		return $conn;
	}

	// 关闭连接
	function close ()
	{
		if ( $this->traceEnabled && $this->tracer )
		{
			$this->tracer->close ();
		}
		$this->inTransaction = false;
		$this->conn = null;
	}

	// 使用数据库
	function useDatabase ( $database, $conn = null )
	{
		if ( !$conn ) $conn = $this->conn;
		if ( !$conn ) return false;
		if ( $database )
		{
			$conn->query ( "USE $database;" );
			$ret = $conn->errorCode () > 0 ? false : true;
			if ( !$ret )
			{
				echo ( "<ERROR><div style='padding:20px;'>无法连接数据库，请稍后访问！</div>" );
				throw ( $e );
			}
			return $ret;
		}
	}

	// 开始事务
	function begin ()
	{
		if ( !$this->conn ) return false;
		if ( !$this->inTransaction )
		{
			$this->conn->beginTransaction ();
			$this->inTransaction = true;
		}
	}

	// 提交事务
	function commit ()
	{
		if ( !$this->conn ) return false;
		if ( $this->inTransaction )
		{
			$this->conn->commit ();
			$this->inTransaction = false;
			$this->begin ();
		}
		if ( $this->debug )
		{
			$this->showDebug ();
		}
	}

	// 回滚事务
	function rollBack ()
	{
		if ( !$this->conn ) return false;
		if ( $this->inTransaction )
		{
			$this->conn->rollBack ();
			$this->inTransaction = false;
			$this->begin ();
		}
	}

	// 输出调试 SQL
	function showDebug ()
	{
		$this->endTime = array_sum ( explode ( ' ', microtime() ) );

		while ( list ( $key, $item ) = @each ( $this->statSql ) )
		{
			echo $item . $this->debugLineSplit;
		}
		echo '<br />Query Time: ' . ( $this->endTime - $this->startTime ) . $this->debugLineSplit;
		if ( $this->conn && $this->conn->errorCode () > 0 )
		{
			$errorInfo = $this->conn->errorInfo ();
			echo 'Error: ' . $this->conn->errorCode () . ' - ' . $errorInfo[2] . $this->debugLineSplit;
		}
		echo $this->debugLineSplit;

		$this->startTime = '';
		$this->endTime = '';
		$this->statSql = array();		
	}

	// 修改数据库记录
	function update ( $dbTable, $condition, $arrUpdate = null, $strUpdate = null, $limit = 0 )
	{
		if ( $condition != '' )
		{
			$condition = "WHERE $condition";
		}
		while ( list ( $key, $item ) = @each ( $arrUpdate ) ) # 连接要更新的字段
		{
			$updateFieldArr[] = " {$this->charSplit}$key{$this->charSplit} = '$item'";
		}
		$updateFields = @join ( ',', $updateFieldArr );
		if ( $strUpdate )
		{
			if ( $updateFields ) $strUpdate .= ', ' . $updateFields;
		}
		else
		{
			$strUpdate = $updateFields;
		}
		$strSql = " UPDATE $dbTable SET $strUpdate $condition";
		if ( $limit > 0 )
		{
			$strSql .= " LIMIT $limit";
		}
		return $this->query ( $strSql );
	}

	// 插入记录
	function insert ( $dbTable, $arrInsert )
	{
		if ( is_array ( current ( $arrInsert ) ) )
		{
			$insertfields = $this->charSplit . join ( $this->charSplit . ', ' . $this->charSplit, array_keys ( current ( $arrInsert ) ) ) . $this->charSplit;
			while ( list ( $key, $item ) = @each ( $arrInsert ) )
			{
				$insertValuesArr[] = "( '" . join ( "', '", $item ) . "' )";
			}
			$insertValues = join ( ', ', $insertValuesArr );
		}
		else
		{
			$insertfields = $this->charSplit . join ( $this->charSplit . ', ' . $this->charSplit, array_keys ( $arrInsert ) ) . $this->charSplit;
			$insertValues = "( '" . join ( "', '", $arrInsert ) . "' )";
		}

		$strSql = "INSERT INTO $dbTable ( $insertfields ) VALUES $insertValues";
	
		return $this->query ( $strSql );
	}

	// 插入记录
	function replace ( $dbTable, $arrInsert )
	{
		if ( is_array ( current ( $arrInsert ) ) )
		{
			$insertfields = $this->charSplit . join ( $this->charSplit . ', ' . $this->charSplit, array_keys ( current ( $arrInsert ) ) ) . $this->charSplit;
			while ( list ( $key, $item ) = @each ( $arrInsert ) )
			{
				$insertValuesArr[] = "( '" . join ( "', '", $item ) . "' )";
			}
			$insertValues = join ( ', ', $insertValuesArr );
		}
		else
		{
			$insertfields = $this->charSplit . join ( $this->charSplit . ', ' . $this->charSplit, array_keys ( $arrInsert ) ) . $this->charSplit;
			$insertValues = "( '" . join ( "', '", $arrInsert ) . "' )";
		}

		$strSql = "REPLACE INTO $dbTable ( $insertfields ) VALUES $insertValues";
	
		return $this->query ( $strSql );
	}

	// 删除数据库记录
	function delete ( $dbTable, $condition = '', $orderBy = '', $limit = 0, $offset = 0  )
	{
		if( $condition != '' )
		{
			$condition = "WHERE $condition";
		}

		$orderBy = trim ( $orderBy );
		if ( $orderBy != '' && !strstr ( strtoupper ( $orderBy ), 'ORDER BY' ) )
		{
			$orderBy = "ORDER BY $orderBy";
		}

		$strSql = "DELETE FROM $dbTable $condition $orderBy";
		
		$limit = intval ( $limit );
		$offset = intval ( $offset );
		if ( $limit )
		{
			$strSql .= " LIMIT $limit";
		}
		if ( $offset )
		{
			$strSql .= " OFFSET $offset";
		}

		return $this->query ( $strSql );
	}

	// 查询数据库记录
	function select ( $dbTable, $condition = '', $orderBy = '', $limit = 0, $offset = 0, $fields = '*', $groupBy = '' )
	{
		if ( is_array ( $fields ) )
		{
			$fieldList = @implode( ',', $fields );
		}
		else
		{
			$fieldList = $fields;
		}
		if ( $condition != '' )
		{
			$condition = "WHERE $condition";
		}
		$orderBy = trim ( $orderBy );
		if ( $orderBy != '' && !strstr ( strtoupper ( $orderBy ), 'ORDER BY' ) )
		{
			$orderBy = "ORDER BY $orderBy";
		}

		$groupBy = trim ( $groupBy );
		if ( $groupBy != '' && !strstr ( strtoupper ( $groupBy ), 'GROUP BY' ) )
		{
			$groupBy = "GROUP BY $groupBy";
		}

		$strSql = " SELECT $fieldList FROM $dbTable $condition $groupBy $orderBy";

		$limit = intval ( $limit );
		$offset = intval ( $offset );
		if ( $limit )
		{
			$strSql .= " LIMIT $limit";
		}
		if ( $offset )
		{
			$strSql .= " OFFSET $offset";
		}
		$res = $this->query ( $strSql );
		
		if ( $res )
		{
			$records = $res->fetchAll ( PDO::FETCH_ASSOC );
			return $records;
		}
		else
		{
			return false;
		}
	}

	// 统计符合条件的记录条数
	function count ( $dbTable, $condition = '', $fields = '*' )
	{
		if ( $condition != '' )
		{
			$condition = "WHERE $condition";
		}
		$strSql = " SELECT COUNT($fields) AS count_records FROM $dbTable $condition";
		$res = $this->query ( $strSql );
		if ( $res )
		{
			$countRecords = $res->fetch ( PDO::FETCH_ASSOC );
			return $countRecords['count_records'];
		}
		else
		{
			return false;
		}
	}

	// 提交数据库查询
	function query ( $strSql )
	{
		$strSql = trim ( $strSql );

		if ( $this->readOnly )
		{
			$writeOperations = $this->writeOperations;
			while ( list ( $key, $item ) = @each ( $writeOperations ) )
			{
				if ( preg_match ( "/^$item/is", $strSql ) )
				{
					$this->affectedRows = 1;
					return true;
				}
			}
		}

		$conn = $this->connect ();
		if ( !$conn ) return false;

		$this->queryCount ++; // 查询次数增加
		$this->affectedRows = 0; // 重置影响行数

		if ( empty ( $this->startTime ) )
		{
			$this->startTime = array_sum ( explode ( ' ', microtime() ) );
		}

		array_push ( $this->statSql, $strSql );
		if ( count ( $this->statSql ) > $this->statSqlLimit ) array_shift ( $this->statSql );

		try
		{
			$res = $conn->query ( $strSql );
		}
		catch ( PDOException $e )
		{
			if ( !$this->ignoreError )
			{
				echo ( "<ERROR><div style='padding:20px;'>服务器忙，请稍后访问！</div>" );
				throw ( $e );
			}
		}

		if ( $conn->errorCode () > 0 ) $res = false;

		if ( !$res )
		{
			$errorInfo = $conn->errorInfo ();

			if ( $this->debug  )
			{
				echo ( 'Error: ' . $errorInfo[2] . '<br /><br />SQL: ' . $strSql );
			}
		}
		else
		{
			$this->affectedRows += intval ( $res->rowCount () );
		}
		return $res;
	}

	// 最后插入 ID
	function lastInsertId ()
	{
		if ( !$this->conn ) return false;
		$lastInsertId = intval ( $this->conn->lastInsertId () );
		return $lastInsertId;
	}

	// 获取错误代码
	function errorCode ( $conn = null )
	{
		if ( !$conn ) $conn = $this->conn;
		if ( !$conn ) return false;
		$errorCode = intval ( $conn->errorCode () );
		return $errorCode;
	}

	// 获取错误信息
	function errorMsg ( $conn = null )
	{
		if ( !$conn ) $conn = $this->conn;
		if ( !$conn ) return false;
		$errorInfo = $conn->errorInfo ();
		$errorMsg = $errorInfo[2];
		return $errorMsg;
	}
}
?>